namespace TransformCodeGenerator
{
  using System;
  using System.IO;
  using System.Reflection;
  using System.Runtime.InteropServices;
  using System.Xml;
  using System.Xml.XPath;
  using System.Xml.Xsl;

  [Guid("B465D0B1-348C-49A4-B061-CD645815BDE7")]
  [CustomTool("TransformCodeGenerator90", "Transform Code Generator for VS2008")]
  public class TransformCodeGenerator: CustomToolBase
  {

    private string _defaultExtension;

    public TransformCodeGenerator()
    {
      _defaultExtension = ".cs";
    }

    public override string DoGenerateCode(string inputFileName, string inputFileContent)
    {

      string transformerFileName = "NOT FOUND";
      StringWriter outputWriter = new StringWriter();
      try
      {

        FileInfo inputFileInfo = new FileInfo(inputFileName);

        // get the source document
        XmlDocument sourceDocument = new XmlDocument();
        sourceDocument.LoadXml(inputFileContent);

        XmlNode documentNode = sourceDocument.DocumentElement;
        if(documentNode==null)
          throw new XmlException("Invalid XML source file.");

        // default extension?
        XmlAttribute defaultExtensionAttrib = documentNode.Attributes["output-extension"];
        if (defaultExtensionAttrib != null)
          _defaultExtension = defaultExtensionAttrib.Value;

        // get the filename of the transformer
        string transformerFile = documentNode.Attributes["transformer"].Value;
        
        // TODO: Replace with resolver pattern

        if(!File.Exists(transformerFileName))
        {
          // try in the same dir as the file
          transformerFileName = Path.Combine(inputFileInfo.DirectoryName, transformerFile);

          if (!File.Exists(transformerFileName))
          {
            // try in the dir where this dll lives
            FileInfo assemblyFileInfo = new FileInfo(Assembly.GetExecutingAssembly().Location);
            transformerFileName = Path.Combine(assemblyFileInfo.DirectoryName, transformerFile);
          }
        }

        // get paths to search for XSLT files for the XMLResolver
        string[] searchPaths = new string[] {
          inputFileInfo.DirectoryName,
          new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName
        };

        // get the xslt document
        XPathDocument transformerDoc = new XPathDocument(transformerFileName);

        // create the transform
        XslCompiledTransform xslTransform = new XslCompiledTransform();
        xslTransform.Load
          (
            transformerDoc.CreateNavigator(), 
            XsltSettings.Default, 
            new TransformCodeGeneratorXmlResolver(searchPaths)
          );

        FileInfo fi = new FileInfo(inputFileName);

        XsltArgumentList args = new XsltArgumentList();

        AssemblyName assemblyName = Assembly.GetExecutingAssembly().GetName();

        args.AddParam("generator", String.Empty, assemblyName.FullName);
        args.AddParam("version", String.Empty, assemblyName.Version.ToString());
        args.AddParam("fullfilename", String.Empty, inputFileName);
        args.AddParam("filename", String.Empty, fi.Name);
        args.AddParam("date-created", String.Empty, DateTime.Today.ToLongDateString());
        args.AddParam("created-by", String.Empty, String.Format("{0}\\{1}", Environment.UserDomainName, Environment.UserName));
        args.AddParam("namespace", String.Empty, base.FileNamespace);
        args.AddParam("classname", String.Empty, fi.Name.Substring(0, fi.Name.LastIndexOf(".")));

        // TODO: add extensions such as eXSLT to the arg list

        // do the transform
        xslTransform.Transform(sourceDocument, args, outputWriter);

      }
      catch(Exception ex)
      {
        outputWriter.WriteLine("/*");
        outputWriter.WriteLine("\tERROR: Unable to generate output for template:");
        outputWriter.WriteLine("\t'{0}'", inputFileName);
        outputWriter.WriteLine("\tUsing transformer:");
        outputWriter.WriteLine("\t'{0}'", transformerFileName);
        outputWriter.WriteLine("");
        outputWriter.WriteLine(ex.ToString());
        outputWriter.WriteLine("*/");
      }

      return outputWriter.ToString();

    }

    public override string GetDefaultExtension()
    {
      return _defaultExtension;
    }

  }

}
